using GriffinNav.GPS.NMEA;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.IO;

namespace GriffinNav.GPS
{
    internal class BluetoothSerial
    {
        internal delegate void DataReceivedHandler(string sentence);
        internal delegate void GenericHandler();

        internal event GenericHandler Started;
        internal event GenericHandler Stopped;
        internal event DataReceivedHandler DataReceived;
        internal event GpsErrorHandler OnError;

        private SerialPort mSerialPort = null;

        private bool mReadData = false;

        private const Parity M_PARITY = Parity.None;
        private const int M_DATABIT = 8;
        private const StopBits M_STOPBIT = StopBits.One;

        internal BluetoothSerial(string port, int baudRate)
        {
            mSerialPort = new SerialPort(port, baudRate, M_PARITY, M_DATABIT, M_STOPBIT);
            
            /*  
                set read timeout to 30 sec since apparently 
                SerialPort.InfiniteTimeout does not work 
                in Compact Framework v2.0
            */ 
            mSerialPort.ReadTimeout = 30000;

            Stopped += new GenericHandler(BluetoothSerial_Stopped);
        }

        internal int ReadTimeout
        {
            set
            {
                mSerialPort.ReadTimeout = value * 1000;
            }
        }

        internal DeviceState State
        {
            get
            {
                // check if device is open and reading data
                if (mReadData && mSerialPort.IsOpen)
                {
                    return DeviceState.Running;
                }

                // device not reading data, check if open
                if (mSerialPort.IsOpen)
                {
                    // close serial port
                    mSerialPort.Close();

                    if (Stopped != null)
                    {
                        Stopped(); // raise Stopped event
                    }
                }

                mReadData = false;

                return DeviceState.Stopped;
            }
        }

        internal void StopRead()
        {
            // check if port is open
            if (mReadData)
            {
                mReadData = false; // set read flag to end read thread
            }
        }

        internal void StartRead()
        {
            try
            {
                // check if the serial port is open
                if (!mSerialPort.IsOpen)
                {
                    mSerialPort.DtrEnable = true;

                    mSerialPort.Open(); // open serial port
                    mReadData = true; // set read flag
                }

                if (Started != null)
                {
                    Started(); // raise Started event
                }

                string line = string.Empty;

                // read while reading allowed and port is open
                while (mReadData && mSerialPort.IsOpen)
                {
                    line = mSerialPort.ReadLine();

                    if (!string.IsNullOrEmpty(line))
                    {
                        if (DataReceived != null)
                        {
                            // raise DataReceived event and pass NMEA sentence
                            DataReceived(line);
                        }

                        Thread.Sleep(50);
                    }
                }
            }
            catch (ThreadAbortException)
            {
                // thread was being aborted
            }
            catch (Exception ex)
            {
                if (OnError != null)
                {
                    OnError(new GPSException(ex, "Error reading GPS data. GPS stopped.", "BluetoothSerial", "StartRead()"), false);
                }
            }
            finally
            {
                if (Stopped != null)
                {
                    Stopped(); // raise Stopped event
                }
            }
            
        }

        private void BluetoothSerial_Stopped()
        {
            try
            {
                // check if the serial port is open
                if (mSerialPort.IsOpen)
                {
                    mSerialPort.Close();
                }
            }
            catch (Exception ex)
            {
                if (OnError != null)
                {
                    OnError(new GPSException(ex, "Error stopping GPS.", "BluetoothSerial", "BluetoothSerial_Stopped()"), false);
                }
            }
            finally
            { }
        }
    }
}
